package cn.fxc;

import org.apache.commons.math3.distribution.FDistribution;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.stat.Frequency;
import org.apache.commons.math3.stat.correlation.Covariance;
import org.apache.commons.math3.stat.correlation.PearsonsCorrelation;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.stat.descriptive.moment.Variance;
import org.apache.commons.math3.stat.inference.TestUtils;
import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression;
import org.apache.commons.math3.stat.regression.SimpleRegression;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class StatisticsExample1 {
    //统计概率
    public static void calc1(){
//        Frequency f = new Frequency();
//        f.addValue(1);
//        f.addValue(new Integer(1));
//        f.addValue(new Long(1));
//        f.addValue(2);
//        f.addValue(new Integer(-1));
//        System.out.println(f.getCount(1));   // displays 3
//        System.out.println(f.getCumPct(1));  // displays 0.2

    }

    //计算线性拟合
    public static void calcSimpleRegression(){
        SimpleRegression regression = new SimpleRegression();
        double[][] data = { { 9,600 }, {7,600 }, {8,700 }, {8,400 }, {2,600 }};
        regression.addData(data);
        System.out.println(regression.getIntercept());
// displays intercept of regression line

        System.out.println(regression.getSlope());
// displays slope of regression line

        System.out.println(regression.getSlopeStdErr());
// displays slope standard error
    }
//计算协方差
    public static void clacCovariance(){
        double[][] data = { { 6,600 }, {6,600 }, {7,700 }, {4,400 }, {2,200 }};
//        double[][] data = { { 9,600 }, {7,600 }, {8,700 }, {8,400 }, {2,600 }};
        double[][] data1 = MatrixUtils.createRealMatrix(data).transpose().getData();
        double covariance = new Covariance().covariance(data1[0], data1[1]);
        System.out.println("covariance:" +covariance);
    }
    //计算相关性
    public static void claccorrelation(){
        double[][] data = { { 6,600 }, {6,600 }, {7,700 }, {4,400 }, {2,200 }};
//        double[][] data = { { 9,600 }, {7,600 }, {8,700 }, {8,400 }, {2,600 }};
        double[][] data1 = MatrixUtils.createRealMatrix(data).transpose().getData();
        double correlation = new PearsonsCorrelation().correlation(data1[0], data1[1]);
        System.out.println("correlation:" +correlation);
    }
    public static void K_STest(){
        double[] doubles = Util.loadData();
        double mean = new Mean().evaluate(doubles);
        System.out.println("均值: "+mean);
        double sd = Math.sqrt(new Variance().evaluate(doubles));
        System.out.println("标准差: "+sd);
        System.out.println("样本数量: "+doubles.length);

        NormalDistribution normalDistribution = new NormalDistribution(mean, sd);
        double KS = TestUtils.kolmogorovSmirnovStatistic(normalDistribution, doubles);
        System.out.println("KS: "+KS);
        //true:pValue: 0.9722054099406334
        //false:pValue: 0.9722055272409958
        double pValue = TestUtils.kolmogorovSmirnovTest(normalDistribution, doubles, false);
        System.out.println("pValue: "+pValue);
    }

    private double calculateVIF(double rSquared) {
        // 避免除以0的情况，当R²接近1时返回最大值
        if (Math.abs(1 - rSquared) < 1e-10) {
            return Double.MAX_VALUE;
        }
        return 1.0 / (1.0 - rSquared);
    }

    public static void  multiplelinearregression(){
        OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
        double[] y = new double[]{53,58,52,40,60,58,77,64,47,59,70,78,59,53,48,57,46,52,47,55,66,45,53,78,41,56,75,45,46,63,45,76,61,79,55,64,54,46,72};
        double[][] x = new double[2][];
        x[0] = new double[]{1,1,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,1,1,1,1,1,0,0,1};
        x[1] = new double[]{179,167,171,164,169,169,175,158,162,167,163,161,170,174,173,168,163,156,173,159,177,178,170,155,161,165,165,158,157,170,155,168,171,169,174,158,176,179,176};
        double[][] xt = MatrixUtils.createRealMatrix(x).transpose().getData();
        regression.newSampleData(y, xt);

        double[] beta = regression.estimateRegressionParameters();
        for (double d:beta)
            System.out.println("beta: "+d);

        double[] estimateRegressionParametersStandardErrors = regression.estimateRegressionParametersStandardErrors();
        for (double d:estimateRegressionParametersStandardErrors)
            System.out.println("estimateRegressionParametersStandardErrors: "+d);
        int degreesOfFreedom = y.length-x.length-1;
        TDistribution tDistribution = new TDistribution(degreesOfFreedom);
        double[] tValue = new double[beta.length];
        double[] pValue = new double[beta.length];
        for (int i=0;i<beta.length;i++){
            tValue[i] = beta[i]/estimateRegressionParametersStandardErrors[i];
            System.out.println("T Value["+i+"]: "+tValue[i]);
            pValue[i] = 2* (1- tDistribution.cumulativeProbability(Math.abs(tValue[i])));
            System.out.println("P Value["+i+"]: "+pValue[i]);
        }

        double SST = regression.calculateTotalSumOfSquares();
        double SSE = regression.calculateResidualSumOfSquares();

        double SSR = SST-SSE;
        double adjMS = SSR/x.length;
//        double RSq = (Math.abs(SST) < 1e-10)?(1-Double.MAX_VALUE):(1.0- (SSE/SST));
//        System.out.println("RSq: "+RSq);
        double dfResidual = y.length - x.length -1;
        double rSquared = regression.calculateRSquared();
//        System.out.println("rSquared: "+rSquared);
        double[]rSquaredi = new double[x.length];
        double[]VIF = new double[x.length];
        double[]SSRxi= new double[x.length];
        double[]adjSSRxi = new double[x.length];
        double[]fxi = new double[x.length];
        double[]pxi = new double[x.length];
        double adjMSe = SSE/dfResidual;
        double dfR = x.length;
        double fValueR = adjMS/adjMSe;
        FDistribution fDistribution = new FDistribution(x.length,y.length-x.length-1);
        double pValueR = 1.0 - fDistribution.cumulativeProbability(fValueR);
        for (int i=0;i<x.length;i++){
            double[] newY = x[i];
            double[][] newX = new double[x.length-1][];
            for (int t=0,s=0;s<x.length;s++){
                if (s!=i){
                    newX[t]= x[s];
                    t++;
                }
            }
            double[][] newXt = MatrixUtils.createRealMatrix(newX).transpose().getData();
            OLSMultipleLinearRegression regressionXi = new OLSMultipleLinearRegression();
            regressionXi.newSampleData(newY, newXt);
            rSquaredi[i]= regressionXi.calculateRSquared();
//            System.out.println("rSquaredi["+i+"]:"+ rSquaredi[i]);
            VIF[i] = 1/(1-rSquaredi[i]);
            System.out.println("VIF["+(i+1)+"]:"+ VIF[i]);

            OLSMultipleLinearRegression regressionYi = new OLSMultipleLinearRegression();
            regressionYi.newSampleData(y,newXt);


            double SSTxi = regressionYi.calculateTotalSumOfSquares();
            double SSExi = regressionYi.calculateResidualSumOfSquares();
            SSRxi[i] = SSTxi-SSExi;
            adjSSRxi[i] = SSR-SSRxi[i];
//            System.out.println("adjSSRxi["+i+"]"+adjSSRxi[i]);
            fxi[i]=adjSSRxi[i]/1.0/adjMSe;
            pxi[i]=1.0-fDistribution.cumulativeProbability(fxi[i]);
        }
        System.out.println();
//
//
//        for (double d:residuals)
//            System.out.println("residuals: "+d);
//        double[][] parametersVariance = regression.estimateRegressionParametersVariance();
//        for (double[] pArray:parametersVariance) {
//            for (double d : pArray) {
//                System.out.println("parametersVariance: " + d);
//            }
//            System.out.println();
//        }
//
//        double regressandVariance = regression.estimateRegressandVariance();
//        System.out.println("regressandVariance: "+regressandVariance);
        double AdjustedRSquared = regression.calculateAdjustedRSquared();

        double sigma = regression.estimateRegressionStandardError();
        System.out.println("S: "+sigma);
        System.out.println("Rsq: "+regression.calculateRSquared());
        System.out.println("Rsq-调整: "+AdjustedRSquared);
        double[][] hat = regression.calculateHat().getData();
        double press = 0;
        for(int i=0;i<y.length;i++){
            press +=Math.pow(regression.estimateResiduals()[i]/(1-hat[i][i]),2);
        }
        double RsqPred = 1.0 - press/SST;
        System.out.println("Rsq-预测: "+RsqPred);


        System.out.println("回归自由度(dfM): "+dfR);
        System.out.println("SSR(回归adjSS): "+SSR);
        System.out.println("回归adjMS: "+adjMS);
        System.out.println("回归FValue: "+fValueR);
        System.out.println("回归PValue: "+pValueR);
        System.out.println();

        for (int i=0;i<x.length;i++){
            System.out.println("第"+(i+1)+"个变量的自由度: 1.0");
            System.out.println("第"+(i+1)+"个变量的adjSS: "+adjSSRxi[i]);
            System.out.println("第"+(i+1)+"个变量的adjMS: "+adjSSRxi[i]/1.0);
            System.out.println("第"+(i+1)+"个变量的FValue: "+fxi[i]);
            System.out.println("第"+(i+1)+"个变量的PValue: "+pxi[i]);

        }

        System.out.println("残差自由度(dfResidual): "+dfResidual);
        System.out.println("SSE(残差adjSS): "+SSE);
        System.out.println("残差adjMS: "+adjMSe);

        TDistribution tDistributionStand = new TDistribution(y.length-x.length-1);
        double criticalValue  = tDistributionStand.inverseCumulativeProbability(1 - 0.05 / 2);
        System.out.println("criticalValue : "+criticalValue );
        List<Parameter> parameterList = new ArrayList<>();
        for (int i=1;i<=x.length;i++){
            parameterList.add(new Parameter(String.valueOf(i),tValue[i]));
        }
        Collections.sort(parameterList, new Comparator<Parameter>() {
            @Override
            public int compare(Parameter o1, Parameter o2) {
                return o2.compareTo(o1);
            }
        });
        for (Parameter p:parameterList){
            System.out.println("name: " + p.name);
            System.out.println("t: " + p.tValue);
            System.out.println();
        }
    }

    static class Parameter implements Comparable{
        public Parameter(String name, double tValue) {
            this.name = name;
            this.tValue = tValue;
        }

        String name;
        double tValue;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public double gettValue() {
            return tValue;
        }

        public void settValue(double tValue) {
            this.tValue = tValue;
        }

        @Override
        public int compareTo(Object o) {
            return Double.compare(this.tValue, ((Parameter)o).tValue);
        }
    }

    public static void main(String[] args) {
        multiplelinearregression();
    }
}
